#include "ip_packet.h"
#include <netdb.h>
#include "extract.h"
#include "ipproto.h"
#include "tcp_segment.h"

IPPacket::IPPacket(u_char *head, u_int len)
{
    ipd._ip = (const ip*)head;
    length = len;
}
#if 0
int IPPacket::print_ipheader()
{
    printf("(tos 0x%x", (int)ipd.ip->ip_tos);
    /* ECN bits */
    if (ipd.ip->ip_tos & 0x03) {
        switch (ipd.ip->ip_tos & 0x03) {
        case 1:
            printf(",ECT(1)");
            break;
        case 2:
            printf(",ECT(0)");
            break;
        case 3:
            printf(",CE");
        }
    }

    if (ipd.ip->ip_ttl >= 1)
        printf(", ttl %u", ipd.ip->ip_ttl);

    /*
                 * for the firewall guys, print id, offset.
                     * On all but the last stick a "+" in the flags portion.
                 * For unfragmented datagrams, note the don't fragment flag.
                 */

    printf(", id %u, offset %u, flags [%s], proto %s (%u)",
           EXTRACT_16BITS(&ipd.ip->ip_id),
           (ipd.off & 0x1fff) * 8,
           bittok2str(ip_frag_values, "none", ipd.off&0xe000),
           tok2str(ipproto_values,"unknown",ipd.ip->ip_p),
           ipd.ip->ip_p);

    printf(", length %u", EXTRACT_16BITS(&ipd.ip->ip_len));

    if ((hlen - sizeof(struct ip)) > 0) {
        printf(", options (");
        ip_optprint(ndo, (u_char *)(ipd.ip + 1), hlen - sizeof(struct ip));
        printf(")");
    }

    if (!ndo->ndo_Kflag && (u_char *)ipd.ip + hlen <= ndo->ndo_snapend) {
        vec[0].ptr = (const uint8_t *)(void *)ipd.ip;
        vec[0].len = hlen;
        sum = in_cksum(vec, 1);
        if (sum != 0) {
            ip_sum = EXTRACT_16BITS(&ipd.ip->ip_sum);
            printf(", bad cksum %x (->%x)!", ip_sum,
                   in_cksum_shouldbe(ip_sum, sum)));
        }
    }

    printf(")\n    ");
}
#endif

void IPPacket::tcp_dissection(const u_char *tcp_begin, u_int len, const u_char *ip, u_int fragmented)
{
    TCPSegment *tcpsegment = new TCPSegment(tcp_begin, len, ip, fragmented);
    tcpsegment->dissection();

}

void IPPacket::ip_demux()
{
    switch (ipd.nh) {
    case IPPROTO_TCP:
        /* pass on the MF bit plus the offset to detect fragments */
        tcp_dissection(ipd.cp, ipd.len, (const u_char *)ipd._ip,
                  ipd.off & (IP_MF|IP_OFFMASK));
        break;

    case IPPROTO_UDP:

    case IPPROTO_AH:

    case IPPROTO_ESP:

    case IPPROTO_IPCOMP:

    case IPPROTO_SCTP:

    case IPPROTO_DCCP:

    case IPPROTO_ICMP:

    case IPPROTO_PIGP:

    case IPPROTO_EIGRP:

    case IPPROTO_ND:

    case IPPROTO_EGP:

    case IPPROTO_OSPF:

    case IPPROTO_IGMP:

    case IPPROTO_IPV4:

    case IPPROTO_IPV6:

    case IPPROTO_RSVP:

    case IPPROTO_GRE:

    case IPPROTO_MOBILE:

    case IPPROTO_PIM:

    case IPPROTO_VRRP:

    case IPPROTO_PGM:

    default:
        printf("nxt ip protocal %d have not implemented\n", ipd.nh);
        break;
    }
}

int IPPacket::dissection()
{
    u_int hlen;
    struct protoent *proto;

    if (IP_V(ipd._ip) != 4) { /* print version if != 4 */
        if (IP_V(ipd._ip) == 6)
        {
            printf("IP6, wrong link-layer encapsulation \n");
        }
        else
        {
            printf("IP%u ", IP_V(ipd._ip));
        }
        return 0;
    }

    if (length < sizeof (struct ip)) {
        printf("truncated-ip %u\n", length);
        return 0;
    }
    hlen = IP_HL(ipd._ip) * 4;
    if (hlen < sizeof (struct ip)) {
        printf("bad-hlen %u", hlen);
        return 0;
    }

    ipd.len = EXTRACT_16BITS(&ipd._ip->ip_len);
    if (length < ipd.len)
        printf("truncated-ip - %u bytes missing! ",
               ipd.len - length);
    if (ipd.len < hlen) {
        printf("bad-len %u", ipd.len);
        return 0;
    }

    /*
         * Cut off the snapshot length to the end of the IP payload.
         */
    ipd.len -= hlen;

    ipd.off = EXTRACT_16BITS(&ipd._ip->ip_off);

    /*
         * If this is fragment zero, hand it to the next higher
         * level protocol.
         */
    if ((ipd.off & 0x1fff) == 0) {
        ipd.cp = (const u_char *)ipd._ip + hlen;
        ipd.nh = ipd._ip->ip_p;

        if (ipd.nh != IPPROTO_TCP && ipd.nh != IPPROTO_UDP &&
                ipd.nh != IPPROTO_SCTP && ipd.nh != IPPROTO_DCCP) {
            printf("%s > %s: ",
                   inet_ntoa(ipd._ip->ip_src),
                   inet_ntoa(ipd._ip->ip_dst));
            return 0;
        }
        ip_demux();
    } else {
        /*
             * if this isn't the first frag, we're missing the
             * next level protocol header.  print the ip addr
             * and the protocol.
             */
        printf("%s > %s: ",
               inet_ntoa(ipd._ip->ip_src),
               inet_ntoa(ipd._ip->ip_dst));
        if ((proto = getprotobynumber(ipd._ip->ip_p)) != NULL)
            printf(" %s\n", proto->p_name);
        else
            printf(" ip-proto-%d\n", ipd._ip->ip_p);
    }
    return 0;
}
